﻿using General.Core.Common;
using General.Core.Common.OAuth.QQ;
using General.Core.Common.OAuth.WeChat;
using General.Core.Config;
using General.Framework;
using General.Mvc.Filters;
using General.Services.Admin;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;

namespace General.Mvc.Controllers.Admin.Public
{
    /// <summary>
    /// 登录方法
    /// </summary>
    [Route("api/admin/public/[controller]/[action]")]
    [ApiController]
    public class LoginController : Controller
    {
        private readonly IAdminService adminService;
        private readonly ILogger<LoginController> logger;
        private readonly IMemoryCache memoryCache;

        public LoginController(IAdminService adminService,
            ILogger<LoginController> logger,
            IMemoryCache memoryCache)
        {
            this.adminService = adminService;
            this.logger = logger;
            this.memoryCache = memoryCache;
        }



        /// <summary>
        /// 管理登录处理方法
        /// </summary>
        /// <param name="UserName"></param>
        /// <param name="Password"></param>
        /// <param name="CaptchaCode"></param>
        /// <returns></returns>
        [HttpPost]
        [ServiceFilter(typeof(ResponseLogTimeFilter))]
        public async Task<IActionResult> LoginProcess([FromForm] string UserName, [FromForm] string Password, [FromForm] string CaptchaCode)
        {
            AjaxResult result = new AjaxResult();
            if (string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(Password))
            {
                result.code = AjaxResultStateEnum.Error;
                result.message = "账号或密码不能为空！";
                return Content(JsonHelper.Serialize(result));
            }
            var SysCode = HttpContext.Session.GetString("CaptchaCode");
            if (string.IsNullOrEmpty(CaptchaCode) || CaptchaCode.Trim() != SysCode)
            {
                result.code = AjaxResultStateEnum.Error;
                result.message = "验证码错误！";
                return Content(JsonHelper.Serialize(result));
            }

            var user = await adminService.GetInfoByUserName(UserName);
            if (user == null)
            {
                result.code = AjaxResultStateEnum.Error;
                result.message = "不存在此账户！";
                return Content(JsonHelper.Serialize(result));
            }
            var ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (string.IsNullOrEmpty(ip))
            {
                ip = HttpContext.Connection.RemoteIpAddress.ToString();
            }
            //进行登录密码解密
            try
            {
                Password = EncryptHelper.AESDecrypt(Password, GobalConfig.AESKEY, GobalConfig.AESIV).Replace("\0", "");
            }
            catch (Exception ex)
            {
                result.code = AjaxResultStateEnum.Error;
                result.message = "密码比对失败，请联系管理员处理！";
                return Content(JsonHelper.Serialize(result));
            }


            if (user.password != EncryptHelper.Md5(Password))
            {
                logger.LogInformation(new Exception("登录失败"), "ip:" + ip.Replace("::ffff:", "") + " 用户名:" + UserName + " 密码:" + Password);
                result.code = AjaxResultStateEnum.Error;
                result.message = "密码错误！";
                return Content(JsonHelper.Serialize(result));
            }
            if (user.status != 1)
            {
                result.code = AjaxResultStateEnum.Error;
                result.message = "您被禁止登录！";
                return Content(JsonHelper.Serialize(result));
            }
            logger.LogInformation(new Exception("登录成功"), "ip:" + ip.Replace("::ffff:", "") + " 用户名:" + UserName + " 密码:" + Password);
            //更新最后登录信息
            await adminService.UpdateLoginStatusInfo(user.id, ip.Replace("::ffff:", ""));

            //存入Session
            HttpContext.Session.SetString("Admin", JsonHelper.Serialize(user));

            result.code = AjaxResultStateEnum.Ok;
            result.message = "登录成功！";
            return Content(JsonHelper.Serialize(result));
        }

        /// <summary>
        /// 获取验证码
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public IActionResult GetCaptchaImage()
        {
            Tuple<string, int> captchaCode = CaptchaHelper.GetCaptchaCode();
            HttpContext.Session.SetString("CaptchaCode", captchaCode.Item2.ToString());
            byte[] bytes = CaptchaHelper.CreateCaptchaImage(captchaCode.Item1);

            return File(bytes, @"image/jpeg");
        }


        /// <summary>
        /// 退出登录
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [ServiceFilter(typeof(ResponseLogTimeFilter))]
        [ServiceFilter(typeof(AdminCheckFilter))]
        public IActionResult ExitLogin()
        {
            HttpContext.Session.Remove("Admin");
            return Redirect("/Admin/Login");
        }


        /// <summary>
        /// QQ登录Authorize
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> QQAuthorize()
        {
            var isOpen = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_STRING + "OpenQQLogin").ToString();
            if (isOpen != "1")
            {
                return Content(new AjaxResult().Error("网站关闭QQ登录！"));
            }
            #region 防止频繁登录
            var ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (string.IsNullOrEmpty(ip))
            {
                ip = HttpContext.Connection.RemoteIpAddress.ToString();
            }
            ip = ip.Replace("::ffff:", "");
            var redisKeyName = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_UNIQUEIDENTIFICATION_STRING);
            var ipLoginRes = await RedisHelper.GetAsync(redisKeyName + GobalConfig.SYSTEM_QQAUTHREDISKEY_STRING + ip);
            if (!string.IsNullOrEmpty(ipLoginRes))
            {
                var ipval = Convert.ToInt32(ipLoginRes);
                if (ipval >= 5)
                {
                    return Content(new AjaxResult().Error("您频繁登录，已被禁止登录3分钟！"));
                }
                else
                {
                    ipval++;
                    await RedisHelper.SetAsync(redisKeyName + GobalConfig.SYSTEM_QQAUTHREDISKEY_STRING + ip, ipval.ToString(), TimeSpan.FromMinutes(3));
                }
            }
            else
            {
                await RedisHelper.SetAsync(redisKeyName + GobalConfig.SYSTEM_QQAUTHREDISKEY_STRING + ip, "1", TimeSpan.FromMinutes(3));
            }
            #endregion


            var guid = Guid.NewGuid().ToString();

            await RedisHelper.SetAsync(redisKeyName + GobalConfig.SYSTEM_QQUSERREDISKEY_STRING + guid, "0", TimeSpan.FromMinutes(10));

            var siteUrl = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_STRING + "Url").ToString();
            var appId = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_STRING + "QQAppId").ToString();
            var result = new AjaxResult()
            {
                code = AjaxResultStateEnum.Ok,
                message = "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id="+ appId + "&redirect_uri="+ siteUrl + "/api/admin/public/Login/QQLoginIn?state=" + guid,
                data = guid
            };
            return Content(JsonHelper.Serialize(result));
        }


        /// <summary>
        /// QQ登录
        /// </summary>
        [HttpGet]
        [ServiceFilter(typeof(ResponseLogTimeFilter))]
        public async Task<IActionResult> QQLoginIn(string code,string state,string access_token = "")
        {
            HttpContext.Response.ContentType = "text/html;charset=utf-8";
            var isOpen = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_STRING + "OpenQQLogin").ToString();
            if (isOpen != "1")
            {
                return Content(ErrorTip("网站关闭QQ登录！"));
            }
            var redisKeyName = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_UNIQUEIDENTIFICATION_STRING);
            var loginVal = await RedisHelper.GetAsync(redisKeyName + GobalConfig.SYSTEM_QQUSERREDISKEY_STRING + state);
            if (string.IsNullOrWhiteSpace(loginVal))
            {
                return Content(ErrorTip("登录超时！请重新登录！"));
            }
            
            if (string.IsNullOrEmpty(access_token))
            {
                var siteUrl = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_STRING + "Url").ToString();
                return Redirect("https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=client_id&client_secret=client_secret&code=" + code + "&redirect_uri=" + siteUrl + "/api/admin/public/Login/QQLoginIn" + code);
            }
            else
            {
                await RedisHelper.DelAsync(redisKeyName + GobalConfig.SYSTEM_QQUSERREDISKEY_STRING + state);
                string resultOpenid = ClientRequest("https://graph.qq.com/oauth2.0/me?access_token=" + access_token, string.Empty, "GET");
                resultOpenid = resultOpenid.Replace("callback(", "");
                resultOpenid = resultOpenid.Substring(0, resultOpenid.Length - 2);
                var openIdModel = JsonConvert.DeserializeObject<OpenIDOAuthModel>(resultOpenid);
                var uid = await adminService.GetIdByOpenId("qq", openIdModel.openid.ToString());
                if (uid == -1)
                {
                    return Content(ErrorTip("登录失败！该账号未绑定微信！"));
                }
                var ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
                if (string.IsNullOrEmpty(ip))
                {
                    ip = HttpContext.Connection.RemoteIpAddress.ToString();
                }
                ip = ip.Replace("::ffff:", "");
                //登录处理
                var user = await adminService.GetInfoById(Convert.ToInt32(loginVal));
                if (user.status != 1)
                {
                    return Content(ErrorTip("您被禁止登录！"));
                }
                logger.LogInformation(new Exception("QQ-登录成功"), "ip:" + ip.Replace("::ffff:", "") + " 用户名:" + user.username);
                //更新最后登录信息
                await adminService.UpdateLoginStatusInfo(user.id, ip.Replace("::ffff:", ""));

                //存入Session
                HttpContext.Session.SetString("Admin", JsonHelper.Serialize(user));
                return Redirect("/Admin/Index");
            }
        }


        /// <summary>
        /// 微信登录生成登录二维码
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [ServiceFilter(typeof(ResponseLogTimeFilter))]
        public async Task<IActionResult> WeChatGenerateQRCode()
        {
            var isOpen = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_STRING+"OpenWechatLogin").ToString();
            if (isOpen!="1")
            {
                return Content(new AjaxResult().Error("网站关闭微信登录！"));
            }
            var ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (string.IsNullOrEmpty(ip))
            {
                ip = HttpContext.Connection.RemoteIpAddress.ToString();
            }
            ip = ip.Replace("::ffff:", "");
            var redisKeyName = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_UNIQUEIDENTIFICATION_STRING);
            var ipLoginRes = await RedisHelper.GetAsync(redisKeyName+ GobalConfig.SYSTEM_WECHATQRREDISKEY_STRING + ip);
            if (!string.IsNullOrEmpty(ipLoginRes))
            {
                var ipval = Convert.ToInt32(ipLoginRes);
                if (ipval>=5)
                {
                    return Content(new AjaxResult().Error("您频繁登录，已被禁止登录3分钟！"));
                }
                else
                {
                    ipval++;
                    await RedisHelper.SetAsync(redisKeyName + GobalConfig.SYSTEM_WECHATQRREDISKEY_STRING + ip,ipval.ToString(), TimeSpan.FromMinutes(3));
                }
            }
            else
            {
                await RedisHelper.SetAsync(redisKeyName + GobalConfig.SYSTEM_WECHATQRREDISKEY_STRING + ip, "1",TimeSpan.FromMinutes(3));
            }


            var guid = Guid.NewGuid().ToString();
            var websiteUrl = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_STRING+ "Url");
            var url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatDefaults.AppId + "&redirect_uri="+ websiteUrl + "/api/admin/public/login/wechatloginin&response_type=code&scope=snsapi_userinfo&state="+ guid + "&connect_redirect=1#wechat_redirect";

            HttpContext.Response.Cookies.Append(GobalConfig.SYSTEM_WECHATUSERREDISKEY_STRING, guid, new CookieOptions
            {
                Expires = DateTime.Now.AddMinutes(20),Secure= true
            });

            await RedisHelper.SetAsync(redisKeyName + GobalConfig.SYSTEM_WECHATUSERREDISKEY_STRING + guid, "0", TimeSpan.FromMinutes(10));


            var result = new AjaxResult()
            {
                code = AjaxResultStateEnum.Ok, message = "/api/qrcode?url=" + StringExtension.UrlEncode(url), data = guid
            };
            return Content(JsonHelper.Serialize(result));
        }




        /// <summary>
        /// 微信刷新登录状态
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> WeChatRefreshStatus()
        {
            var isOpen = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_STRING + "OpenWechatLogin").ToString();
            if (isOpen != "1")
            {
                return Content(new AjaxResult().Fail("网站关闭微信登录！"));
            }
            var value = "";
            HttpContext.Request.Cookies.TryGetValue(GobalConfig.SYSTEM_WECHATUSERREDISKEY_STRING, out value);
            var redisKeyName = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_UNIQUEIDENTIFICATION_STRING);
            var loginVal = await RedisHelper.GetAsync(redisKeyName + GobalConfig.SYSTEM_WECHATUSERREDISKEY_STRING + value);
            if (string.IsNullOrWhiteSpace(loginVal))
            {
                return Content(new AjaxResult().Fail("超时！请重新扫码！"));
            }
            if (loginVal == "0")
            {
                return Content(new AjaxResult().Error("请扫码！"));
            }
            else
            {
                if (loginVal=="-1")
                {
                    return Content(new AjaxResult().Fail("登录失败！该账号未绑定微信！"));
                }
                var ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
                if (string.IsNullOrEmpty(ip))
                {
                    ip = HttpContext.Connection.RemoteIpAddress.ToString();
                }
                ip = ip.Replace("::ffff:", "");
                //登录处理
                var user = await adminService.GetInfoById(Convert.ToInt32(loginVal));
                if (user.status != 1)
                {
                    return Content(new AjaxResult().Fail("您被禁止登录！"));
                }
                logger.LogInformation(new Exception("微信-登录成功"), "ip:" + ip.Replace("::ffff:", "") + " 用户名:" + user.username);
                //更新最后登录信息
                await adminService.UpdateLoginStatusInfo(user.id, ip.Replace("::ffff:", ""));

                //清理
                await RedisHelper.DelAsync(redisKeyName + GobalConfig.SYSTEM_WECHATUSERREDISKEY_STRING + value);

                //存入Session
                HttpContext.Session.SetString("Admin", JsonHelper.Serialize(user));
                return Content(new AjaxResult().Ok("登录成功！"));
            }
        }


        /// <summary>
        /// 微信跳转授权登录
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [ServiceFilter(typeof(ResponseLogTimeFilter))]
        public async Task<IActionResult> WeChatLoginIn(string code,string state)
        {
            try
            {
                HttpContext.Response.ContentType = "text/html;charset=utf-8";
                var isOpen = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_STRING + "OpenWechatLogin").ToString();
                if (isOpen != "1")
                {
                    return Content("<script>alert('网站关闭微信登录！')</script>");
                }
                if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(state))
                {
                    return Content("<script>alert('登录失败')</script>");
                }

                var redisKeyName = memoryCache.Get(GobalConfig.SYSTEM_CONFIG_UNIQUEIDENTIFICATION_STRING);
                var loginVal = await RedisHelper.GetAsync(redisKeyName + GobalConfig.SYSTEM_WECHATUSERREDISKEY_STRING + state);
                if (string.IsNullOrWhiteSpace(loginVal))
                {
                    return Content("<script>alert('二维码过期，请重新扫码登录！')</script>");
                }

                Random random = new Random(Environment.TickCount);
                //string tokenUri = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=wxf1f84ee1c835dc1e&secret=a0b7f9516303199ea3fe3b39f7a930b0&code=0110XdHa1yrN4A0gojHa1kJ4ww40XdHe&state=state&grant_type=authorization_code";
                string tokenUri = WeChatDefaults.AccessTokenEndpoint
                    + "?appid=" + WeChatDefaults.AppId
                    + "&secret=" + WeChatDefaults.AppSecret
                    + "&code=" + code//code为前端传入参数，自行获取
                    + "&state=" + state
                    + "&grant_type=authorization_code";

                string resultAccessToken = ClientRequest(tokenUri, string.Empty, "POST");
                if (!string.IsNullOrEmpty(resultAccessToken))
                {
                    WeChatTocken weChatTocken1 = new WeChatTocken();
                    weChatTocken1 = JsonConvert.DeserializeObject<WeChatTocken>(resultAccessToken);
                    if (!string.IsNullOrEmpty(weChatTocken1.Access_Token))
                    {
                        string userUri = WeChatDefaults.UserInformationEndpoint
                            + "?access_token="
                            + weChatTocken1.Access_Token
                            + "&openid=" + weChatTocken1.OpenId;

                        string resultUserInfo = ClientRequest(userUri, string.Empty, "POST");
                        if (!string.IsNullOrEmpty(resultUserInfo))
                        {
                            WeChatUserInfo weChatUserInfo1 = new WeChatUserInfo();
                            weChatUserInfo1 = JsonConvert.DeserializeObject<WeChatUserInfo>(resultUserInfo);
                            if (!string.IsNullOrEmpty(weChatUserInfo1.OpenId))
                            {
                               
                                
                                var uid = await adminService.GetIdByOpenId("wechat", weChatUserInfo1.OpenId);
                                await RedisHelper.SetAsync(redisKeyName + GobalConfig.SYSTEM_WECHATUSERREDISKEY_STRING + state, uid, TimeSpan.FromMinutes(10));

                                return Content("<script>alert('扫码成功，请在网页中查看！')</script>");
                            }
                            else
                            {
                                return Ok("<script>alert('扫码失败，请联系网站管理员！')</script>");
                            }
                        }
                        else
                        {
                            logger.LogInformation("请求微信用户基本信息失败");
                            return Content("<script>alert('请求微信用户基本信息失败')</script>");
                        }
                    }
                    else
                    {
                        logger.LogInformation("请求access_token失败");
                        return Content("<script>alert('请求access_token失败')</script>");
                    }
                }
                return Content("<script>alert('登录失败')</script>");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }



        /// <summary>
        /// 发送请求（get/post/http/https）
        /// </summary>
        /// <param name="uri">请求地址</param>
        /// <param name="JsonStr">json数据</param>
        /// <param name="Method">请求方式POST/GET</param>
        /// <returns></returns>
        public string ClientRequest(string uri, string JsonStr, string Method = "POST")
        {
            try
            {
                var httpRequest = (HttpWebRequest)HttpWebRequest.Create(uri);
                httpRequest.Method = Method;
                httpRequest.ContentType = "application/json";
                if (Method.ToLower() == "get")
                {
                    httpRequest.ContentType = "application/x-www-form-urlencoded";
                }
                httpRequest.Proxy = null;
                httpRequest.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13";
                httpRequest.Headers.Add("Accept-Language", "zh-cn,en-us;q=0.8,zh-hk;q=0.6,ja;q=0.4,zh;q=0.2");
                httpRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";

                //如果是发送HTTPS请求  
                if (uri.StartsWith("https", StringComparison.OrdinalIgnoreCase))
                {
                    ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
                    httpRequest.ProtocolVersion = HttpVersion.Version10;
                    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
                }
                else
                {
                    ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
                    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
                }

                if (!string.IsNullOrEmpty(JsonStr))
                {
                    using (var dataStream = new StreamWriter(httpRequest.GetRequestStream()))
                    {
                        dataStream.Write(JsonStr);
                        dataStream.Flush();
                        dataStream.Close();
                    }
                }

                var httpResponse = (HttpWebResponse)httpRequest.GetResponse();
                using (var dataStream = new StreamReader(httpResponse.GetResponseStream()))
                {
                    var result = dataStream.ReadToEnd();
                    return result;
                }
            }
            catch (Exception ex)
            {
                return "{\"error\":\"" + ex.Message + "\"}";
            }
        }

        private bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
            //throw new NotImplementedException();
        }

        /// <summary>
        /// 登录错误显示内容
        /// </summary>
        /// <returns></returns>
        private string ErrorTip(string content)
        {
            return content + "<script>alert('" + content + "');setTimeout(function() {window.location.href='/admin/login'}, 3000);</script>";
        }
    }
}
